package com.ruoyi.app.service.impl;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.app.aspect.PermissionAspect;
import com.ruoyi.app.domain.*;
import com.ruoyi.app.dto.LoginUserDto;
import com.ruoyi.app.dto.MemberDto;
import com.ruoyi.app.dto.PayDto;
import com.ruoyi.app.dto.RewardDetail;
import com.ruoyi.app.enume.OrderStatusEnum;
import com.ruoyi.app.mapper.MemberMapper;
import com.ruoyi.app.mapper.OrderInfoMapper;
import com.ruoyi.app.mapper.RelUserMemberMapper;
import com.ruoyi.app.service.PayService;
import com.ruoyi.app.service.UserService;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.RedisKeyConstant;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ParamMapUtils;
import com.ruoyi.framework.config.DefaultConfig;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.java.core.cipher.PrivacyEncryptor;
import com.wechat.pay.java.core.http.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.security.Signature;
import java.util.*;
import java.util.concurrent.locks.Lock;

/*
@description:用户支付服务
@ClassName PayServiceImpl
@author chen
@create 2025-03-16 22:59
@Version 1.0
*/
@Slf4j
@Service
public class PayServiceImpl implements PayService
{

    @Resource
    OrderInfoMapper orderInfoMapper;
    @Resource
    MemberMapper memberMapper;
    @Resource
    UserService userService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    @Qualifier
    private RelUserMemberMapper relUserMemberMapper;
    @Resource
    @Qualifier("wxPayNoSignClient")
    private CloseableHttpClient wxPayClient;

    @Resource
    @Qualifier("transferHttpClient")
    private HttpClient httpClient;

    @Resource
    @Qualifier("encryptor")
    private PrivacyEncryptor encryptor;


    // 奖励规则配置
    private static final Map<Integer, Map<Integer, BigDecimal>> REWARD_RULES = new HashMap<>();
    static
    {
        // 49元会员:第1级20,第2级5,第3级0
        Map<Integer, BigDecimal> rule49 = new HashMap<>();
        rule49.put(1, new BigDecimal("20"));
        rule49.put(2, new BigDecimal("5"));
        rule49.put(3, BigDecimal.ZERO);
        REWARD_RULES.put(49, rule49);

        // 199元会员:第1级80,第2级20,第3级0
        Map<Integer, BigDecimal> rule199 = new HashMap<>();
        rule199.put(1, new BigDecimal("80"));
        rule199.put(2, new BigDecimal("20"));
        rule199.put(3, BigDecimal.ZERO);
        REWARD_RULES.put(199, rule199);

        // 888元会员:第1级288,第2级90,第3级0
        Map<Integer, BigDecimal> rule888 = new HashMap<>();
        rule888.put(1, new BigDecimal("288"));
        rule888.put(2, new BigDecimal("90"));
        rule888.put(3, BigDecimal.ZERO);
        REWARD_RULES.put(888, rule888);
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户下单
     * @Date 2025-3-16 23:00
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult order(PayDto payDto) throws Exception
    {
        // 获取用户登录的信息
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        Member member = memberMapper.selectByPrimaryKey(payDto.getMemberId());
        BigDecimal memberPrice = member.getMemberPrice();

        if (Math.abs(payDto.getMoney().subtract(memberPrice).doubleValue()) > 0.01)
        {
            return AjaxResult.error("页面提交价格与实际会员价格不符!");
        }

        // 创建订单信息
         OrderInfo orderInfo = this.creatOrder(payDto);

        // 微信预付单
        ParamMapUtils returnMap = this.toPay(orderInfo.getOrderNo(),payDto.getMoney(),loginUserDto.getWxId(),payDto.getNote());

        return AjaxResult.success(returnMap);
    }

    /*
     * @==============History===============<br/>
     * @Description //   支付回调
     * @Date 2025-3-31 19:50
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public void pay(String orderNo, HttpServletResponse response,ParamMapUtils paramMapUtils)
    {
        Lock lock = redissonClient.getLock(RedisKeyConstant.ORDER_LOCK + orderNo);
        try {
            lock.lock();
            OrderInfoExample orderInfoExample = new OrderInfoExample();
            orderInfoExample.createCriteria().andOrderNoEqualTo(orderNo).andOrderStateEqualTo(OrderStatusEnum.CREATE_NEW.getCode().toString());
            List<OrderInfo> orderInfos = orderInfoMapper.selectByExample(orderInfoExample);
            if(null != orderInfos && orderInfos.size() > 0)
            {
                OrderInfo orderInfo = orderInfos.get(0);
                User currentUser  = userService.selectByPrimaryKey(orderInfo.getCreateId());// 当前购买用户信息
                Member member = memberMapper.selectByPrimaryKey(orderInfo.getMemberId());// 当前购买的会员信息

                // 1.绑定会员关系
                insertSelectiveRelUser(currentUser,member);

                Boolean isDivide = member.getIsDivide();// 当前购买会员是否参与分成
                if(isDivide)
                {
                    log.info("--------------------------------------->>>>为当前用户上级开始计算分红,{}",currentUser.getId());
                    // 2. 获取最多三级的上级
                    int maxLevel = 3;
                    List<User> superiors = getSuperiors(currentUser, maxLevel);

                    // 3.计算奖励
                    List<RewardDetail> rewards = calculateRewards(member.getMemberPrice(), superiors);

                    // 4.发放奖励并更新数据库
                    distributeRewards(rewards);
                }

                // 5.更新订单状态
                OrderInfo updateOrderInfo = new OrderInfo();
                updateOrderInfo.setId(orderInfo.getId());
                updateOrderInfo.setOrderState(OrderStatusEnum.PAYED.getCode().toString());
                orderInfoMapper.updateByPrimaryKeySelective(updateOrderInfo);

                // 6.刷新当前用户token
                LoginUserDto loginUser = new LoginUserDto();
                BeanUtils.copyProperties(currentUser,loginUser);
                MemberDto memberDto = relUserMemberMapper.selectMemberInfoByUserId(loginUser.getId());
                loginUser.setMemberInfo(memberDto);
                userService.refreshToken(loginUser);

                // 成功应答
                response.setStatus(200);
                Objects.requireNonNull(paramMapUtils.put("code", "SUCCESS")).put("message","成功");
            }else
            {
                // 失败应答
                response.setStatus(500);
                Objects.requireNonNull(paramMapUtils.put("code", "ERROR")).put("message","当前订单已支付,无需处理");
            }
        }catch (Exception exception)
        {
            exception.printStackTrace();
            throw new BaseException("绑定会员关系失败");
        }finally {
            lock.unlock();
        }
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户提现
     * @Date 2025-4-11 18:53
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.framework.web.domain.AjaxResult
     */
    @Override
    public AjaxResult transfer(PayDto payDto)
    {
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        User user = userService.selectByPrimaryKey(loginUserDto.getId());
        BigDecimal balance = user.getBalance();

        if (balance.compareTo(payDto.getMoney()) < 0)
        {
            return AjaxResult.error("提现金额大于用户余额!");
        }


        // 创建订单
        OrderInfo orderInfo = this.creatOrder(payDto);

        // 提现
        String result = this.toTransfer(orderInfo.getOrderNo(), user.getWxId(), payDto.getUserName(),payDto.getMoney());

        return AjaxResult.success(result);
    }

    /*
     * @==============History===============<br/>
     * @Description //   用户提现回调
     * @Date 2025-4-11 20:46
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return void
     */
    @Override
    public void transferBill(String orderNo, HttpServletResponse response, ParamMapUtils paramMapUtils)
    {
        Lock lock = redissonClient.getLock(RedisKeyConstant.ORDER_LOCK + orderNo);
        try {
            lock.lock();
            OrderInfoExample orderInfoExample = new OrderInfoExample();
            orderInfoExample.createCriteria().andOrderNoEqualTo(orderNo).andOrderStateEqualTo(OrderStatusEnum.CREATE_NEW.getCode().toString());
            List<OrderInfo> orderInfos = orderInfoMapper.selectByExample(orderInfoExample);
            if(null != orderInfos && orderInfos.size() > 0)
            {
                OrderInfo orderInfo = orderInfos.get(0);
                User currentUser  = userService.selectByPrimaryKey(orderInfo.getCreateId());// 当前购买用户信息

                // 更新订单状态
                OrderInfo updateOrderInfo = new OrderInfo();
                updateOrderInfo.setId(orderInfo.getId());
                updateOrderInfo.setOrderState(OrderStatusEnum.PAYED.getCode().toString());
                orderInfoMapper.updateByPrimaryKeySelective(updateOrderInfo);

                // 刷新当前用户token
                LoginUserDto loginUser = new LoginUserDto();
                BeanUtils.copyProperties(currentUser,loginUser);
                MemberDto memberDto = relUserMemberMapper.selectMemberInfoByUserId(loginUser.getId());
                loginUser.setMemberInfo(memberDto);
                userService.refreshToken(loginUser);

                // 成功应答
                response.setStatus(200);
                Objects.requireNonNull(paramMapUtils.put("code", "SUCCESS")).put("message","成功");
            }else
            {
                // 失败应答
                response.setStatus(500);
                Objects.requireNonNull(paramMapUtils.put("code", "ERROR")).put("message","当前订单已提现成功,无需处理");
            }
        }catch (Exception exception)
        {
            exception.printStackTrace();
            throw new BaseException("用户提现失败!");
        }finally {
            lock.unlock();
        }
    }



    /*
     * @Description //   提现
     * @Date 2025-4-11 20:47
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return java.lang.String
     */
    private String toTransfer(String orderNo,String openid,String userName,BigDecimal money)
    {
        HttpHeaders headers = new HttpHeaders();
        headers.addHeader("Accept", MediaType.APPLICATION_JSON.getValue());
        headers.addHeader("Content-Type", MediaType.APPLICATION_JSON.getValue());
        headers.addHeader("Wechatpay-Serial", encryptor.getWechatpaySerial());

        ParamMapUtils paramMapUtils = new ParamMapUtils();


        Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(paramMapUtils.put("appid", Constants.APPLET_APPID))
                                                                        .put("out_bill_no", orderNo))
                                                                .put("transfer_scene_id", "1000"))
                                                        .put("openid", openid))
                                                .put("user_name", encryptor.encrypt(userName)))
                                        .put("transfer_amount", money.multiply(new BigDecimal(100)).intValue()))
                                .put("transfer_remark", "提现"))
                        .put("notify_url", Constants.NOTIFY_URL + "/app/transferBill"))
                .put("user_recv_perception", "提成奖励");



        // 转账场景报备信息
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(new JSONObject().fluentPut("info_type", "活动名称").fluentPut("info_content", "用户提现"));
        jsonArray.add(new JSONObject().fluentPut("info_type", "奖励说明").fluentPut("info_content", "用户提现"));

        paramMapUtils.put("transfer_scene_report_infos", jsonArray);


        JsonRequestBody build = new JsonRequestBody.Builder()
                .body(JSONUtil.toJsonStr(paramMapUtils))
                .build();

        HttpRequest executeSendGetHttpRequest = new HttpRequest.Builder()
                .httpMethod(HttpMethod.POST)
                .url("https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills")
                .headers(headers)
                .body(build)
                .build();

        try {
            HttpResponse<JSONObject> execute = httpClient.execute(executeSendGetHttpRequest, JSONObject.class);
            JSONObject responseBody = execute.getServiceResponse();
            log.info("发起提现返回：{}", responseBody.toJSONString());
            return responseBody.toJSONString();
        }  catch (Exception e)
        {
            e.printStackTrace();
            throw new BaseException(e.getMessage());
        }
    }


    /*
     * @==============History===============<br/>;
     * @Description //   绑定会员关系
     * @Date 2025/4/2
     * @Author < a href="chencheng@prmt.net">chen</ a>;
     */
    private void insertSelectiveRelUser(User currentUser, Member member)
    {
        RelUserMember relUserMember = new RelUserMember();
        relUserMember.setCreateTime(new Date());
        relUserMember.setDisable(false);
        relUserMember.setUpdateTime(new Date());
        relUserMember.setUserId(currentUser.getId());
        relUserMember.setMemberId(member.getId());
        relUserMember.setExpirationTime(DateUtils.addMonthWithLocalDate(new Date(),member.getMemberDate()));
        relUserMemberMapper.insertSelective(relUserMember);
    }


    /*
     * @==============History===============<br/>
     * @Description //   创建订单
     * @Date 2025-3-31 19:50
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return com.ruoyi.app.domain.OrderInfo
     */
    private OrderInfo creatOrder(PayDto payDto)
    {
        // 获取当前登陆人
        LoginUserDto loginUserDto = PermissionAspect.loginUser.get();
        OrderInfo orderInfo = new OrderInfo();
        String orderNo = UUID.randomUUID().toString().replace("-","");
        log.info("用户下单生成订单号:" + orderNo);
        orderInfo.setOrderNo(orderNo);
        orderInfo.setCreateTime(new Date());
        orderInfo.setUpdateTime(new Date());
        orderInfo.setMemberId(payDto.getMemberId());
        orderInfo.setCreateId(loginUserDto.getId());
        orderInfo.setUpdateId(loginUserDto.getId());
        orderInfo.setMoney(payDto.getMoney());
        orderInfo.setNote(payDto.getNote());
        orderInfo.setOrderState(OrderStatusEnum.CREATE_NEW.getCode().toString());
        orderInfoMapper.insertSelective(orderInfo);
        return orderInfo;
    }



    /*
     * @==============History===============<br/>
     * @Description //   微信预付单
     * @Date 2025-4-2 0:43
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return java.util.Map<java.lang.String,java.lang.String>
     */
    private ParamMapUtils toPay(String orderNo, BigDecimal money, String openid,String note) throws Exception
    {
        // 统一下单api
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");

        // 支付者
        ParamMapUtils playerMapUtils = new ParamMapUtils()
                                          .put("openid",openid);

        // 订单金额
        ParamMapUtils amountMapUtils = Objects.requireNonNull(new ParamMapUtils()
                           .put("total", money.multiply(new BigDecimal(100)).intValue()))// 金额
                        .put("currency","CNY");// 货币类型

        // 请求参数
        ParamMapUtils paramMapUtils = Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(new ParamMapUtils()
                                                                .put("appid", Constants.APPLET_APPID))//appid
                                                        .put("mchid", Constants.APPLET_MCH_ID))//商户号
                                                .put("description", note))// 商品描述
                                        .put("out_trade_no", orderNo))//商户订单号
                                .put("notify_url", Constants.NOTIFY_URL + "/app/pay"))//通知地址
                        .put("amount", amountMapUtils))// 金额
                      .put("payer",playerMapUtils);// 支付者


        String jsonParams = JSON.toJSONString(paramMapUtils);

        log.info("微信预下单请求参数:"+jsonParams);


        StringEntity entity = new StringEntity(jsonParams,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        // 完成签名并执行请求
        CloseableHttpResponse resp = wxPayClient.execute(httpPost);

        int statusCode = resp.getStatusLine().getStatusCode();// 响应码
        String bodyAsString = EntityUtils.toString(resp.getEntity());// 响应报文

        if(statusCode == 200)
        {
            log.info("微信预付单成功,返回结果 = " + bodyAsString);
        }
        else if(statusCode == 204)
        {
            log.info("微信预付单成功,无返回结果");
        }else
        {
            log.error("小程序下单失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
            throw new BaseException("小程序下单失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
        }

        JSONObject jsonObject = JSON.parseObject(bodyAsString);
        long currentTimestamp = System.currentTimeMillis();
        String nonceStr = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
        String packageStr = "prepay_id=" + jsonObject.getString("prepay_id");
        String aPackage = Constants.APPLET_APPID + "\n" + currentTimestamp + "\n" + nonceStr + "\n" + packageStr + "\n";


        Signature sign = Signature.getInstance("SHA256withRSA"); //SHA256withRSA
        InputStream inputStream = DefaultConfig.class.getClassLoader().getResourceAsStream("client/apiclient_key.pem");
        assert inputStream != null;
        sign.initSign(PemUtil.loadPrivateKey(inputStream)); // 微信证书私钥
        sign.update(aPackage.getBytes(StandardCharsets.UTF_8));
        String paySign = Base64.getEncoder().encodeToString(sign.sign());

        return Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(Objects.requireNonNull(new ParamMapUtils()
                                                        .put("appid", Constants.APPLET_APPID))
                                                .put("timeStamp", currentTimestamp + ""))
                                        .put("nonceStr", nonceStr))
                                .put("signType", "RSA"))
                        .put("package", packageStr))
                .put("paySign", paySign);
    }


    /*
     * @==============History===============<br/>;
     * @Description //   获取上级链(最多maxLevel层)
     * @Date 2025/4/2 
     * @Author < a href="chencheng@prmt.net">chen</ a>;
     */
    private List<User> getSuperiors(User user, int maxLevel)
    {
        List<User> superiors = new ArrayList<>();
        User current = user;
        int level = 0;

        while (current != null && level < maxLevel)
        {
            // 根据parentId查询上级用户
            User superior = userService.selectByPrimaryKey(current.getParentId());
            if (superior == null)
                break;
            superiors.add(superior);
            current = superior;
            level++;
        }
        return superiors;
    }


    /*
     * @==============History===============<br/>;
     * @Description //   计算各层奖励
     * @Date 2025/4/2 
     * @Author < a href="chencheng@prmt.net">chen</ a>;
     */
    private List<RewardDetail> calculateRewards(BigDecimal amount, List<User> superiors)
    {
        int priceKey = amount.setScale(0, RoundingMode.DOWN).intValue(); // 转换为整数
        Map<Integer, BigDecimal> rule = REWARD_RULES.get(priceKey);

        if (rule == null)
        {
            return Collections.emptyList();
        }

        List<RewardDetail> rewards = new ArrayList<>();
        for (int i = 0; i < superiors.size(); i++)
        {
            int level = i + 1; // 层级从1开始
            BigDecimal reward = rule.getOrDefault(level, BigDecimal.ZERO);
            if (reward.compareTo(BigDecimal.ZERO) > 0)
            {
                rewards.add(new RewardDetail(superiors.get(i).getId(), reward));
            }
        }
        return rewards;
    }



    /*
     * @==============History===============<br/>
     * @Description //   实际发放奖励
     * @Date 2025-3-31 21:04
     * @Author < a href="15991488967@163.com">chen</ a>
     * @return void
     */
    private void distributeRewards(List<RewardDetail> rewards)
    {
        for (RewardDetail detail : rewards)
        {
            log.info("更新上级分红------------------->>>>用户id:" + detail.getUserId() + ",用户分红:" + detail.getAmount());
            userService.addBalance(detail.getUserId(), detail.getAmount());
        }
    }


}
